package game_engine.display;

import static org.lwjgl.opengl.GL11.GL_BLEND;
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_TEST;
import static org.lwjgl.opengl.GL11.GL_MODELVIEW;
import static org.lwjgl.opengl.GL11.GL_ONE_MINUS_SRC_ALPHA;
import static org.lwjgl.opengl.GL11.GL_PROJECTION;
import static org.lwjgl.opengl.GL11.GL_SRC_ALPHA;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D;
import static org.lwjgl.opengl.GL11.glBlendFunc;
import static org.lwjgl.opengl.GL11.glClear;
import static org.lwjgl.opengl.GL11.glDisable;
import static org.lwjgl.opengl.GL11.glEnable;
import static org.lwjgl.opengl.GL11.glLoadIdentity;
import static org.lwjgl.opengl.GL11.glMatrixMode;
import static org.lwjgl.opengl.GL11.glOrtho;
import static org.lwjgl.opengl.GL11.glViewport;
import game_engine.GameEngine;
import game_engine.core.GECore;

import java.awt.Color;
import java.awt.Dimension;
import java.io.IOException;

import org.lwjgl.LWJGLException;
import org.lwjgl.Sys;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.newdawn.slick.TrueTypeFont;

public class GEWindowOGL implements Runnable {

	private Dimension resolution;

	private TextureLoader textureLoader;
	private FontLoader fontLoader;

	private GECore core;

	// private static InputHandler input;

	private boolean isRunning = true;
	public int fps = 60;
	private String title;

	public long tick = 0;
	public long lastTick = 0;
	private long timerTicksPerSecond = Sys.getTimerResolution();
	private long lastLoopTime = getTime();
	private long lastFpsTime;

	private boolean loaded = false;

	private Thread renderingThread;

	private DisplayMode displayMode;
	private boolean fullscreen = false;

	public GEWindowOGL(GECore core) {
		this.core = core;
		renderingThread = new Thread(this, "RenderingThread");
	}

	public void setResolution(Dimension resolution) {
		this.resolution = resolution;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public void graphicalRender(long tick) {
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		core.render();
		// GEWindowOGL.drawString(1000/tick+" fps", resolution.width-50, 40, 15,
		// Color.RED, 1.0f);
		Display.update();
	}

	private void initialize() {
		Display.setTitle(title);
		try {
			Display.setFullscreen(fullscreen);
			DisplayMode d[] = Display.getAvailableDisplayModes();
			for (int i = 0; i < d.length; i++) {
				if (d[i].getWidth() == resolution.width && d[i].getHeight() == resolution.height && d[i].getBitsPerPixel() == 32) {
					displayMode = d[i];
					break;
				}
			}
			Display.setDisplayMode(displayMode);
			Display.create();

			glEnable(GL_TEXTURE_2D);

			GL11.glShadeModel(GL11.GL_SMOOTH);

			glEnable(GL_BLEND);
			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

			// disable the OpenGL depth test since we're rendering 2D graphics
			glDisable(GL_DEPTH_TEST);

			glMatrixMode(GL_PROJECTION);
			glLoadIdentity();

			glOrtho(0, resolution.width, resolution.height, 0, -1, 1);

			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();

			glViewport(0, 0, resolution.width, resolution.height);

			textureLoader = new TextureLoader();
			fontLoader = new FontLoader();

			while (!Display.isCreated()) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			while (!Display.isActive()) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}

		} catch (LWJGLException e1) {
			e1.printStackTrace();
		}
	}

	private void loadTextures() {
		Object[] texture;
		while ((texture = core.getTextureToLoad()) != null) {
			try {
				textureLoader.getTexture((String) texture[0], (Color) texture[1]);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		core.setTextureLoaded(true);
	}

	private void loadFonts() {
		Object[] fonts;
		while ((fonts = core.getFontToLoad()) != null) {
			fontLoader.addFont((String) fonts[0], (String) fonts[1], (Integer) fonts[2]);
		}
		core.setFontLoaded(true);
	}

	public TrueTypeFont getFont(String id) {
		return fontLoader.getFont(id);
	}

	public Texture getTexture(String image, Color transColor) {
		try {
			return textureLoader.getTexture(image, transColor);
		} catch (IOException e) {
			System.err.println("IMAGE " + image + " FAILED TO LOAD.");
			e.printStackTrace();
		}
		return null;
	}

	private void updateTick() {

		// SystemTimer.sleep(lastLoopTime+10-SystemTimer.getTime());
		Display.sync(60);

		// work out how long its been since the last update, this
		// will be used to calculate how far the entities should
		// move this loop
		tick = getTime() - lastLoopTime;
		lastLoopTime = getTime();
		lastFpsTime += tick;
		fps++;
		// update our FPS counter if a second has passed
		if (lastFpsTime >= 1000) {
			Display.setTitle(title + " (FPS: " + fps + ", Memory: " + (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024 / 1024
					+ "Mo.)");
			lastFpsTime = 0;
			fps = 0;
		}
	}

	public long getTime() {
		// we get the "timer ticks" from the high resolution timer
		// multiply by 1000 so our end result is in milliseconds
		// then divide by the number of ticks in a second giving
		// us a nice clear time in milliseconds
		return (Sys.getTime() * 1000) / timerTicksPerSecond;
	}

	public boolean isLoaded() {
		return loaded;
	}

	public void run() {
		initialize();
		loaded = true;
		while (isRunning) {
			if (!core.isAllTextureLoaded())
				loadTextures();
			if (!core.isAllFontLoaded())
				loadFonts();
			updateTick();
			graphicalRender(tick);
			keyUpdate();
		}
		GameEngine.getInstance().stop();
		Display.destroy();
	}

	public void start() {
		renderingThread.start();
	}
	
	public void stop() {
		isRunning = false;
	}

	private void keyUpdate() {
		if (Keyboard.isKeyDown(Keyboard.KEY_LMENU) && Keyboard.isKeyDown(Keyboard.KEY_F4)) {
			isRunning = false;
		}
		if (Keyboard.isKeyDown(Keyboard.KEY_LMENU) && Keyboard.isKeyDown(Keyboard.KEY_RETURN)) {
			try {
				fullscreen = !fullscreen;
				Display.setFullscreen(fullscreen);
			} catch (LWJGLException e) {
				e.printStackTrace();
			}
		}
	}
}
